package com.uwlaser.socket.client;

import com.uwlaser.socket.exception.TcpSocketException;
import com.uwlaser.socket.exception.TcpSocketTimeoutException;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;

@Data
@Slf4j
public class BaseNettyMVBoxClient {

	private String remoteIp;
	private Integer remotePort;

	//局部变量化关键变量，方便使用
	private EventLoopGroup group;
	private Bootstrap b;
	private ChannelFuture cf;



	public BaseNettyMVBoxClient(String ip, Integer port) {
		this.remoteIp = ip;
		this.remotePort = port;

		group = new NioEventLoopGroup();
		b = new Bootstrap();
		b.group(group)
			.option(ChannelOption.SO_KEEPALIVE, true)
			.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,3000)
			.channel(NioSocketChannel.class)
			.handler(new LoggingHandler(LogLevel.INFO))//开启日志
			.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel sc) throws Exception {
					/*ByteBuf byteBuf = Unpooled.copiedBuffer("$===+++end+++===$".getBytes());
					sc.pipeline().addLast(new DelimiterBasedFrameDecoder(4096,byteBuf));*/
//					设置字符串解码器，将buf转化为字符串,默认是u8。handler接收到的就是string类型的
					sc.pipeline().addLast(new StringDecoder(StandardCharsets.UTF_8));
					sc.pipeline().addLast(new PrintHandler());
				}
			});
	}

	/**
	 * 客户端对象执行连接的逻辑
	 */
	private void connect() throws Exception {
		try {
			this.cf = b.connect(this.remoteIp, this.remotePort).sync();
			//log.debug("远程服务器【"+this.remoteIp+"】【"+this.remotePort+"】已经连接, 可以进行数据交换..");
		} catch (Exception e) {
			shutdown();
			throw new TcpSocketTimeoutException("连接远程服务器【"+this.remoteIp+"】【"+this.remotePort+"】时发生异常："+ e.getMessage());
		}
	}

	/**
	 * 实现断线重连，发送数据通过cf对象，就可以启用重连
	 */
	private ChannelFuture getChannelFuture() throws Exception {

		if(this.cf == null){
			this.connect();
		}
		if(!this.cf.channel().isActive()){
			this.connect();
		}
		return this.cf;
	}

	public String sendMsg(ByteBuf msg) throws Exception {
		String result = null;
		try {
			Channel channel = this.getChannelFuture().channel();
			String key = getId(channel);
			SocketResultMap.setKey(key);
			channel.writeAndFlush(msg);
			//阻塞等待服务器响应
			result = SocketResultMap.getResult(key);

			//自定义等待超时时间
			//result = SocketResultMap.getResult(key,10 * 1000);
		}catch (Exception e){
			if(e instanceof TcpSocketException){
				throw e;
			} else {
				throw new TcpSocketTimeoutException("发送数据时发生异常【"+this.remoteIp+"】【"+this.remotePort+"】："+ e.getMessage());
			}
		}
		return result;
	}

	public void shutdown(){
		if (this.cf != null){
			this.cf.channel().close();
		}
		if (this.group!=null){
			this.group.shutdownGracefully();
		}
		if(this.b != null){
			this.b = null;
		}
	}

	/**
	 * 获取ID
	 * @param channel
	 * @return
	 */
	public static String getId(Channel channel){
		ChannelId id = channel.id();
		String key = id.asShortText() == null ? id.asLongText() : id.asShortText();
		return key;
	}

}